package com.chenu.gamscriptman.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.chenu.gamscriptman.compoent.Global;
import com.chenu.gamscriptman.dao.*;
import com.chenu.gamscriptman.domain.*;
import com.chenu.gamscriptman.domain.noindb.*;
import com.chenu.gamscriptman.service.DeviceTask;
import com.chenu.gamscriptman.service.GameDataTask;
import com.chenu.gamscriptman.service.NotifyService;
import com.chenu.gamscriptman.utils.TimeUtils;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import static com.chenu.gamscriptman.domain.Device.groupNrs;

/**
 * @author flyan
 * date 2020-01-02
 * @function
 */
@CacheConfig(cacheNames = "gamedata")   /* 本类使用缓存名称：gamedata */
@Service("gameDataTask")
public class GameDataTaskImpl implements GameDataTask {

    private Logger logger = LoggerFactory.getLogger(this.getClass());

    /* 请求游戏数据服务的服务路由键 */
    public static final String SERVICE_ROUTING_KEY = "gamedata";

    /* 消息服务器的游戏数据消息队列 */
    public static final String QUEUE = "gamscriptman.gamedatatask.queue";

    /* 数据库数据是否被更新了 */
    public static boolean DbIsUpdate = true;

    /* redis模板，用于缓存和处理结果的暂存 */
    @Autowired
    private RedisTemplate jsonRedisTemplate;

    /* gson，用于处理json数据 */
    @Autowired
    private Gson gson;
    private Gson gameDataGson = new GsonBuilder()

            .setExclusionStrategies(new ExclusionStrategy() {

                /* 设置需要忽略的字段 */
                @Override
                public boolean shouldSkipField(FieldAttributes f) {
                    return GameData.ignoreAttr.contains(f.getName());
                }

                /* 设置需要清空的字段类型 */
                @Override
                public boolean shouldSkipClass(Class<?> clazz) {
                    return false;
                }
            }).create();

    /* 其他任务 */
    @Autowired
    NotifyService notifyService;
    @Autowired
    DeviceTask deviceTask;

    /* 事务管理器 */
    @Autowired
    DataSourceTransactionManager transactionManager;

    /* dao */
    @Autowired
    GameDataDao gameDataDao;
    @Autowired
    GameTaskDao gameTaskDao;
    @Autowired
    UserDao userDao;
    @Autowired
    GameDao gameDao;
    @Autowired
    UserDataDao userDataDao;
    @Autowired
    DeviceDao deviceDao;

    /* 任务能提供的所有功能索引号 */
    public static final int GAMEDATA_LIST = 0;                /* 得到所有游戏数据信息 */
    public static final int GAMEDATA_ADD_OR_EDIT_VIEW = 1;    /* 前往游戏数据添加/编辑页面 */
    public static final int GAMEDATA_ADD_OR_UPDATE = 2;       /* 游戏数据添加或更新 */
    public static final int GAMEDATA_REMOVE = 3;              /* 游戏数据删除 */
    public static final int GAMEDATA_COUNT = 4;               /* 得到游戏数据数量 */
    public static final int GAMEDATA_GET = 5;                 /* 得到一条游戏数据信息，可通过游戏数据id/游戏数据号/MAC地址 */

    /* 任务入口，等待一条消息并处理，然后回发一条包含处理结果的消息 */
    @Override
    @RabbitListener(queues = QUEUE)
    public Message task(Message message) {
        Message rs;

        /* 看看想做什么？ */
        switch (message.getType()){
            case GAMEDATA_LIST:                 /* 列出所有游戏数据 */
                logger.debug("游戏数据任务：GAMEDATA_LIST...");
                rs = do_list(message);
                break;

            case GAMEDATA_ADD_OR_EDIT_VIEW:     /* 需要前往游戏数据添加或编辑页面 */
                logger.debug("游戏数据任务：GAMEDATA_ADD_OR_EDIT_VIEW...");
                rs = do_add_or_edit_view(message);
                break;

            case GAMEDATA_ADD_OR_UPDATE:        /* 添加或更新一个游戏数据 */
                logger.debug("游戏数据任务：GAMEDATA_ADD_OR_UPDATE...");
                rs = do_add_or_update(message);
                break;

            case GAMEDATA_REMOVE:               /* 删除一个游戏数据 */
                logger.debug("游戏数据任务：GAMEDATA_REMOVE...");
                rs = do_remove(message);
                break;

            case GAMEDATA_COUNT:               /* 得到游戏数据数量 */
                logger.debug("游戏数据任务：GAMEDATA_COUNT...");
                rs = do_count(message);
                break;

            case GAMEDATA_GET:                    /* 得到一条游戏数据信息 */
                logger.debug("游戏数据任务：GAMEDATA_GET...");
                rs = do_get(message);
                break;

            default:
                rs = do_no_call(message);
                break;
        }

        /* 返回包含处理结果的消息 */
        return rs;
    }

    private Message do_list(Message message) {
        PageBean<GameData> data = null;
        Statistics statistics = null;   /* 统计信息 */
        String rtData = null;

        /* 解析消息中的数据 */
        ListMsgParameter<GameData> parameter = gson.fromJson(message.getData(), new TypeToken<ListMsgParameter<GameData>>() {}.getType());
        if(parameter != null && parameter.getPageBean() != null){    /* 不为null才需要 */
            parameter.setPageBean(new PageBean<GameData>(
                    parameter.getPageBean().getCurrent(),
                    parameter.getPageBean().getSize(),
                    true,
                    parameter.getPageBean().getDynamicHopeSize()
            ));
        }

        /* 获取所有游戏名称列表 */
        List<String> gameNameList = new ArrayList<>();
        for (Game game:
                gameDao.selectList(new QueryWrapper<Game>()
                        .select("name"))) {
            gameNameList.add(game.getName());
        }

        /* 检查查询参数 */
        boolean paging = false;     /* 是否需要分页？ */
        if(gameNameList.size() != 0){
            if(parameter == null) { /* 没有任何限制，查看游戏数据分组按游戏，默认游戏名称为第一个 */
                parameter = new ListMsgParameter<GameData>()
                        .setPageBean(null)
                        .setQueryBean(new GameData().setGameName(gameNameList.get(0)));
            } else {
                /* 保证按游戏分组查询，没有就按游戏名称的第一个游戏 */
                if(parameter.getQueryBean() == null){
                    parameter.setQueryBean(new GameData().setGameName(gameNameList.get(0)));
                } else if(StringUtils.isEmpty(parameter.getQueryBean().getGameName())){
                    parameter.setQueryBean(parameter.getQueryBean().setGameName(gameNameList.get(0)));
                }
                paging = true;
            }
        } else {
            /* 还没有任何游戏，那么游戏数据也理应没有 */
            parameter = new ListMsgParameter<GameData>()
                    .setQueryBean(new GameData()
                            .setGameName(""));
        }

        /* 检查是否可以使用更快速的缓存 */
        GameData queryBean = (GameData) jsonRedisTemplate.opsForValue().get(Global.REDIS_SEARCH_GAMEDATA);
        PageBean<GameData> pageBean = (PageBean<GameData>)jsonRedisTemplate.opsForValue().get(Global.REDIS_GAMEDATA_PAGES);
        if(new ListMsgParameter<GameData>()
                .setEx_int(null)
                .setQueryBean(queryBean)
                .setPageBean(pageBean)
                .equals(parameter) && !DbIsUpdate){
            logger.debug("游戏数据列表 --> 使用缓存");
            /* 查询条件一样，而且数据库近期没更新，那么不需要去数据库拿了，缓存中有 */
            return MessageServiceImpl.TaskFinish(
                    message,
                    (long) MessageServiceImpl.GAMEDATA_TASK,
                    Message.HANDLER_SUCCESS,
                    "/gamedatas"
            );
        }
        logger.debug("游戏数据列表 --> 数据库查询");

        /* 查询，这一步只有上面的缓存用不到才会执行到 */
        data = findAll(parameter.getPageBean(), parameter.getQueryBean());
        if(paging) data.calcDynamicBar();  /* 计算动态显示条 */

        /* 现在得到统计信息 */
        statistics = new Statistics().
                setNowTotal(gameDataDao.selectCount(null)).
                setNowUpdated(0);

        /* 查询失败了，直接返回错误 */
        if(data.getRecords() == null){
            return MessageServiceImpl.TaskFinish(
                    message,
                    (long) MessageServiceImpl.GAMEDATA_TASK,
                    Message.HANDLER_FAILURE,
                    "游戏数据任务：GAMEDATA_LIST失败......"
            );
        }

        /* 现在解析映射 */
        Game game = null;
        GameMap gameConfigMap = null;
        if(gameNameList.size() != 0){
            game = gameDao.selectOne(new QueryWrapper<Game>()
                    .select("config_map")
                    .eq("name", parameter.getQueryBean().getGameName()));
            gameConfigMap = gson.fromJson(game.getConfigMap(), GameMap.class);
            jsonRedisTemplate.opsForValue().set(Global.REDIS_GAMEDATA_MAP, gameConfigMap);
            logger.debug("Global.REDIS_GAMEDATA_MAP --> " + gameConfigMap);
            /* 然后解析资源数据 */
            for (GameData g :
                    data.getRecords()) {
                Map<String, String> map = gson.fromJson(g.getData(), new TypeToken<Map<String, String>>() {}.getType());
                g.setResourceDataMap(map);
                logger.debug("--> " + map);
            }
        }


        /* 成功处理 */
        if(message.isNeedView()){
            /* 如果需要视图，将结果、参数放入缓存，并设置返回的页面 */
            jsonRedisTemplate.opsForValue().set(Global.REDIS_GAMEDATA_PAGES, data);
            jsonRedisTemplate.opsForValue().set(Global.REDIS_SEARCH_GAMEDATA, parameter.getQueryBean());
            /* 还有统计信息 */
            jsonRedisTemplate.opsForValue().set(Global.REDIS_GAMEDATA_STATISTICS, statistics);
            /* 以及所有的游戏名 */
            jsonRedisTemplate.opsForValue().set(Global.REDIS_SEARCH_GAME_NAME_LIST, gameNameList);
            rtData = "/gamedatas";
        } else {
            /* 不需要视图，将结果转化为json串放到回复消息中 */
            rtData = gson.toJson(new ListResult<GameData>().setPageBean(data).setStatistics(statistics));
            logger.warn("--> " + rtData);
        }

        /* 返回结果 */
        return MessageServiceImpl.TaskFinish(
                message,
                (long) MessageServiceImpl.GAMEDATA_TASK,
                Message.HANDLER_SUCCESS,
                rtData
        );
    }

    private Message do_add_or_edit_view(Message message) {
        GameData gameData = null;
        Long id = null;
        String rtData;

        try{
            id = Long.valueOf(message.getData());
        } catch (Exception e){
            logger.info("用户前往的是添加页面...");
        }

        if(id != null){
            /* 如果有id，则说明是前往编辑页面，查询该条游戏数据信息 */
            gameData = gameDataDao.selectById(id);
            /* 得到游戏数据映射 */
            gameData.setResourceDataMap(gson.fromJson(gameData.getData(), new TypeToken<Map<String, String>>() {}.getType()));
        }

        /* 因为添加/更新需要js做一些解析工作，所以游戏数据映射还是用json字符串比较方便，现在我们放入 */
        GameData queryBean = (GameData) jsonRedisTemplate.opsForValue().get(Global.REDIS_SEARCH_GAMEDATA);
        Game game = gameDao.selectOne(new QueryWrapper<Game>()
                .select("config_map")
                .eq("name", queryBean.getGameName()));
        jsonRedisTemplate.opsForValue().set(Global.REDIS_GAMEDATA_MAP_JSON, game.getConfigMap());

        /* 需要视图？ */
        if(message.isNeedView()){
            /* 放入要编辑的游戏 */
            jsonRedisTemplate.opsForValue().set(Global.REDIS_EDIT_GAMEDATA, gameData);
            /* 设置跳转页面 */
            rtData = "/gamedata";
        } else {
            /* 不需要视图，将编辑的用户转为json串 */
            rtData = gson.toJson(gameData);
        }

        /* 这个调用必定成功 */
        return MessageServiceImpl.TaskFinish(
                message,
                (long) MessageServiceImpl.GAMEDATA_TASK,
                Message.HANDLER_SUCCESS,
                rtData
        );
    }

    private Message do_add_or_update(Message message) {
        int rows;
        boolean isInsert = false;
        int handleState;
        String returnData;

        /* 获取事务定义 */
        DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();
        /* 设置事务隔离级别 */
        transDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        /* 得到事务状态并开启事务 */
        TransactionStatus transStatus = transactionManager.getTransaction(transDef);

        /* 解析消息 */
        try {
            GameData gameData = gson.fromJson(message.getData(), GameData.class);
            if(gameData.getId() == null) isInsert = true;

            /* 将所有中文符号(， “”  ：  {}  )转换为英文符号 */
            if(!StringUtils.isEmpty(gameData.getDevRange())){
                gameData.setDevRange(com.chenu.gamscriptman.utils.StringUtils.replaceChineseCharacter(gameData.getDevRange()));
            }
            if(!StringUtils.isEmpty(gameData.getRegionServerRange())){
                gameData.setRegionServerRange(com.chenu.gamscriptman.utils.StringUtils.replaceChineseCharacter(gameData.getRegionServerRange()));
            }

            /* 快速检查添加的游戏数据json正不正确 */
            try {
                logger.debug("游戏数据 --> " +
                        gson.fromJson(gameData.getData(), new TypeToken<Map<String, String>>() {}.getType()));
            } catch (JsonParseException e){
                handleState = Message.HANDLER_FAILURE;
                returnData = "游戏数据任务：数据Json不正确，请检查重试...";
                return MessageServiceImpl.TaskFinish(
                        message,
                        (long) MessageServiceImpl.GAMEDATA_TASK,
                        handleState,
                        returnData
                );
            }

            logger.debug(isInsert ? "do_add --> " : "do_update --> " + gameData);
            /* 执行添加和更新
             */
            if(isInsert){
                try {
                    /* 我们先解析用户给出的游戏数据范围 */
                    gameData.setDevRange(gameData.getDevRange().replace(" ", ""));
                    gameData.setRegionServerRange(gameData.getRegionServerRange().replace(" ", ""));
                    String[] ranges = gameData.getDevRange().split(",");
                    String[] reseRanges = gameData.getRegionServerRange().split(",");
                    if(ranges.length == 0){                 /* 给出的游戏数据设备范围是错误的 */
                        handleState = Message.HANDLER_FAILURE;
                        returnData = "游戏数据任务：给出的游戏数据范围[" + gameData.getDevRange() + "]不正确，无法添加游戏数据，" +
                                "有可能包含无效字符，请检查。";
                        return MessageServiceImpl.TaskFinish(
                                message,
                                (long) MessageServiceImpl.GAMEDATA_TASK,
                                handleState,
                                returnData
                        );
                    } else if(reseRanges.length == 0) {     /* 给出的游戏数据区服范围是错误的 */
                        handleState = Message.HANDLER_FAILURE;
                        returnData = "游戏数据任务：给出的区服范围[" + gameData.getRegionServerRange() + "]不正确，无法添加游戏数据，" +
                                "有可能包含无效字符，请检查。";
                        return MessageServiceImpl.TaskFinish(
                                message,
                                (long) MessageServiceImpl.GAMEDATA_TASK,
                                handleState,
                                returnData
                        );
                    } else {
                        /* 当一个游戏数据添加，我们这里什么都不用做，只需要检查数据是否正确并插入游戏数据即可
                         * 当一台设备被调度时，才产生一个真正的游戏任务
                         */
                        /* 得到游戏Id */
                        String gameName = ((GameData)jsonRedisTemplate.opsForValue().get(Global.REDIS_SEARCH_GAMEDATA)).getGameName();
                        Long gameId = gameDao.selectOne(new QueryWrapper<Game>()
                                .select("id")
                                .eq("name", gameName)).getId();
                        rows = save(gameData.setGameFK(gameId));    /* 先创建游戏数据 */
                        if(rows > 0){
                            /* 现在检查设备范围是否正确 */
                            for (String range: ranges) {
                                String[] mulOrSingleRange = range.split("-");
                                boolean isMul;
                                if(mulOrSingleRange.length == 2){
                                    isMul = true;
                                } else if (mulOrSingleRange.length == 1){
                                    isMul = false;
                                } else {
                                    /* 如果错误，回滚事务，所有操作取消 */
                                    transactionManager.rollback(transStatus);
                                    handleState = Message.HANDLER_FAILURE;
                                    returnData = "游戏数据任务：给出的游戏数据范围[" + gameData.getDevRange() + "]不正确，无法添加游戏数据，" +
                                            "有可能包含无效字符，请检查。";
                                    return MessageServiceImpl.TaskFinish(
                                            message,
                                            (long) MessageServiceImpl.GAMEDATA_TASK,
                                            handleState,
                                            returnData
                                    );
                                }
                                if(isMul){      /* 多范围，即 A-B 这样的格式 */
                                    logger.debug("device ranges --> Mul");
                                    /* 先算出范围前后的设备号 */
                                    DevNr p1DevNr = new DevNr();
                                    deviceTask.getDevice(mulOrSingleRange[0], p1DevNr);
                                    DevNr p2DevNr = new DevNr();
                                    deviceTask.getDevice(mulOrSingleRange[1], p2DevNr);
                                    /* 要么像 P1-P2 这样都分组，要么像 1-2 这样都不分组，不允许这样 P1-2 一边分组另一边不分组 */
                                    if(!p1DevNr.getGroupNr().equals(p2DevNr.getGroupNr())){
                                        handleState = Message.HANDLER_FAILURE;
                                        returnData = "游戏数据任务：给出的游戏数据范围[" + gameData.getDevRange() + "]不正确，无法添加游戏数据，" +
                                                "范围要么都分组，要么都不分组，不允许出现一边分组另一边不分组的范围！";
                                        return MessageServiceImpl.TaskFinish(
                                                message,
                                                (long) MessageServiceImpl.GAMEDATA_TASK,
                                                handleState,
                                                returnData
                                        );
                                    }
                                    /* 检查设备号始末是否正常，分组和不分组判断不一样 */
                                    logger.debug("device ranges --> " + mulOrSingleRange[0] + " - "
                                            + mulOrSingleRange[1] + "<-- ");
                                } else {        /* 单范围，即只指出单个游戏数据 */
                                    logger.debug("device ranges --> Single(" + mulOrSingleRange[0] + ") <--");
                                }
                            }
                            /* 现在检查区服范围是否正确 */
                            for (String regin_server: reseRanges) {
                                String[] rs = regin_server.split(":");
                                logger.debug("region_server ranges --> " + Long.valueOf(rs[0]) + " : "
                                        + rs[1] + "<-- ");
                            }
                        }
                    }
                } catch (Exception e){
                    /* 如果出现异常，回滚事务，所有操作取消 */
                    transactionManager.rollback(transStatus);
                    handleState = Message.HANDLER_FAILURE;
                    returnData = "游戏数据任务：给出的游戏数据范围[" + gameData.getDevRange() + "]或者" +
                            "区服范围[" + gameData.getRegionServerRange() + "]不正确，无法添加游戏数据，" +
                            "有可能包含无效字符或格式不正确，请检查。";
                    return MessageServiceImpl.TaskFinish(
                            message,
                            (long) MessageServiceImpl.GAMEDATA_TASK,
                            handleState,
                            returnData
                    );
                }
            } else {
                rows = update(gameData);
            }

            if(rows > 0){   /* 处理成功 */
                notifyService.notify(message.getSource(),
                        isInsert ? "添加游戏数据成功！" : "更新游戏数据（" + gameData.getId() + "）成功！");
                /* 数据已经更新，得到上次的分页数据和查询数据，模仿调用LIST */
                PageBean<GameData> gamedata_page = (PageBean<GameData>) jsonRedisTemplate.opsForValue().get(Global.REDIS_GAMEDATA_PAGES);
                GameData search_gamedata = (GameData) jsonRedisTemplate.opsForValue().get(Global.REDIS_SEARCH_GAMEDATA);
                ListMsgParameter<GameData> parameter = new ListMsgParameter();
                parameter.setPageBean(gamedata_page).setQueryBean(search_gamedata);
                message.setData(gson.toJson(parameter));
                do_list(message);
                handleState = message.getType();
                returnData = message.getData();
            } else {        /* 处理失败 */
                handleState = Message.HANDLER_FAILURE;
                returnData = isInsert ? "游戏数据任务：添加游戏数据失败，请重试..." : "游戏数据任务：更新游戏数据失败，请重试...";
            }

            /* 提交事务 */
            transactionManager.commit(transStatus);

        } catch (Exception e){
            /* 如果出现异常，回滚事务，所有操作取消 */
            transactionManager.rollback(transStatus);
            handleState = Message.HANDLER_FAILURE;
            returnData = "游戏数据任务：添加或更新的游戏数据不正确，请检查...";
        }

        /* 返回处理结果 */
        return MessageServiceImpl.TaskFinish(
                message,
                (long) MessageServiceImpl.GAMEDATA_TASK,
                handleState,
                returnData
        );
    }

//    private void saveGameTaskOfGameData(Message message, GameData gameData, String gameName, Long devNr) {
//        for (Device device:
//                deviceDao.selectList(new QueryWrapper<Device>()
//                        .select("id", "group_nr")
//                        .eq("dev_nr", devNr))) {
//            logger.debug("添加 --> " + device.getGroupNr() + devNr);
//            /* 这一步也很重要，该设备将会被调度，更新该设备的运行游戏名称 */
//            deviceDao.update(new Device().setRunGame(gameName),
//                    new UpdateWrapper<Device>()
//                            .eq("id", device.getId()));
//            /* 创建用户数据 */
//            userDataDao.insert(new UserData()
//                    .setGameName(gameName)
//                    .setData("{}")  /* 刚开始，数据字段还是空的 */
//                    .setUptime(TimeUtils.date2UnixStamp(new Date()))
//                    .setAdminFk(message.getSource()));
//            /* 创建游戏任务 */
//            gameTaskDao.insert(new GameTask()
//                    .setGroupNr(device.getGroupNr())
//                    .setDevNr(devNr)
//                    .setRegion(gameData.getGameTask().getRegion())
//                    .setServer(gameData.getGameTask().getServer())
//                    .setRole(gameData.getGameTask().getRole())
//                    .setGameDataFK(gameDataDao.selectNewId())
//                    .setUserDataFK(userDataDao.selectNewId()));
//        }
//    }

    private Message do_remove(Message message) {
        /* 获取事务定义 */
        DefaultTransactionDefinition transDef = new DefaultTransactionDefinition();
        /* 设置事务隔离级别 */
        transDef.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        /* 得到事务状态并开启事务 */
        TransactionStatus transStatus = transactionManager.getTransaction(transDef);

        int rows = 0;
        try {
            Long id = Long.valueOf(message.getData());
            List<GameTask> tasks = gameTaskDao.selectList(new QueryWrapper<GameTask>()
                    .select("user_data_fk")
                    .eq("game_data_fk", id));
            /* 删除前，必须删除包含这个游戏数据的游戏任务 */
            gameTaskDao.delete(new UpdateWrapper<GameTask>().eq("game_data_fk", id));
            /* 用户数据由游戏数据产生，所以，我们也必须删除这条游戏数据产生的用户数据 */
            for (GameTask t :
                    tasks) {
                userDataDao.deleteById(t.getUserDataFK());
            }
            /* 用户数据已经发生了改动 */
            UserDataTaskImpl.DbIsUpdate = true;

            rows = remove(id);
            if(rows > 0){
                /* 给用户一条通知提示成功提示信息 */
                notifyService.notify(message.getSource(), "游戏数据（" + id + "）删除成功！");
                /* 数据已经更新，得到上次的分页数据和查询数据，模仿调用LIST */
                PageBean<GameData> gamedata_page = (PageBean<GameData>) jsonRedisTemplate.opsForValue().get(Global.REDIS_GAMEDATA_PAGES);
                GameData search_gamedata = (GameData) jsonRedisTemplate.opsForValue().get(Global.REDIS_SEARCH_GAMEDATA);
                ListMsgParameter<GameData> parameter = new ListMsgParameter();
                parameter.setPageBean(gamedata_page).setQueryBean(search_gamedata);
                message.setData(gson.toJson(parameter));
                /* 提交事务 */
                transactionManager.commit(transStatus);
                return do_list(message);
            } else {
                /* 如果出现异常，回滚事务，所有操作取消 */
                transactionManager.rollback(transStatus);
                return MessageServiceImpl.TaskFinish(
                        message,
                        (long) MessageServiceImpl.GAMEDATA_TASK,
                        Message.HANDLER_FAILURE,
                        "游戏数据任务：删除失败，这个游戏数据可能不存在或已经被移除，请重试..."
                );
            }

        } catch (Exception e){
            /* 如果出现异常，回滚事务，所有操作取消 */
            transactionManager.rollback(transStatus);
            return MessageServiceImpl.TaskFinish(
                    message,
                    (long) MessageServiceImpl.USERDATA_TASK,
                    Message.HANDLER_FAILURE,
                    "游戏数据任务：删除失败，你未指明任何游戏数据号"
            );
        }
    }

    private Message do_count(Message message) {
        return MessageServiceImpl.TaskFinish(
                message,
                (long) MessageServiceImpl.GAMEDATA_TASK,
                Message.HANDLER_SUCCESS,
                String.valueOf(gameDataDao.selectCount(null))
        );
    }

    /**
     * 获取一个游戏数据要运行的游戏数据
     * @param message
     * @return
     */
    private Message do_get(Message message) {
        int handleState;
        String returnData;
        Device device = null;
        GameData gameData = null;

        /* 解析消息中的游戏数据数据 */
        String queryGroupNr = null;
        Long queryDevNr = null;
        try {
            Device queryBean = gson.fromJson(message.getData(), Device.class);
            logger.debug("获取游戏数据，条件 --> " + queryBean);
            /* 检查查询条件，优先级：游戏数据id/游戏数据号/MAC地址 */
            if(queryBean.getId() != null){              /* id */
                device = deviceDao.selectOne(new QueryWrapper<Device>()
                        .select("dev_nr")
                        .eq("id", queryBean.getId()));
            } else if(queryBean.getSdevNr() != null){    /* 设备号 */
                device = deviceTask.getDevice(queryBean.getSdevNr(), null);
            } else if(queryBean.getMac() != null){      /* MAC */
                device = deviceDao.selectOne(new QueryWrapper<Device>()
                        .select("dev_nr")
                        .eq("mac", queryBean.getMac()));
            }

            /* 设备是否存在？ */
            if(device == null){
                handleState = Message.HANDLER_FAILURE;
                returnData = "游戏数据任务：没有找到该设备，无法获取它的游戏数据，请先通过/device/save_or_update接口上传该设备...";
            } else {
                /* 设备存在，现在可以查找这个设备的游戏数据了！ */
                /* 检查游戏数据中的游戏数据范围，得到该设备应该使用的游戏数据
                 * 它的规则是：从最新的游戏数据查找到最旧的游戏数据，如果其中某个设备范围中包含了该设备，那么
                 * 这条游戏数据就是我们需要的，拿出来；如果查到最后都莫得包含此设备，那么说明这个设备并没有得
                 * 到一个任务，查无此设备的游戏数据。
                 */
                boolean fond = false;       /* 找到没？ */
                List<GameData> gameDataList = gameDataDao.selectList(new QueryWrapper<GameData>()
                        .orderByDesc("id"));    /* desc倒序 */
                logger.debug("所有的游戏数据(新到旧排序) --> " + gameDataList);
                for (GameData gd:
                        gameDataList) {
                    if(fond) break;     /* 如果已经找到，停止解析 */
                    /* 首先解析设备范围 */
                    String[] ranges = gd.getDevRange().split(",");
                    if(ranges.length == 0){ /* 这个游戏数据范围是错误不存在的 */
                        handleState = Message.HANDLER_FAILURE;
                        returnData = "游戏数据任务：游戏数据中存在不正确的游戏数据范围(" + gd.getDevRange()+ ")，无法继续运行，请先修复该条游戏数据。";
                        return MessageServiceImpl.TaskFinish(
                                message,
                                (long) MessageServiceImpl.USERDATA_TASK,
                                handleState,
                                returnData
                        );
                    } else {                /* 游戏范围正常 */
                        queryGroupNr = device.getGroupNr();
                        queryDevNr = device.getDevNr();
                        /* 现在判断需要拿取的游戏数据存在该游戏数据范围中吗？ */
                        for (String range: ranges) {
                            String[] mulOrSingleRange = range.split("-");
                            boolean isMul;
                            if(mulOrSingleRange.length == 2){
                                isMul = true;
                            } else if (mulOrSingleRange.length == 1){
                                isMul = false;
                            } else {
                                handleState = Message.HANDLER_FAILURE;
                                returnData = "游戏数据任务：游戏数据中有游戏数据范围(" + gd.getDevRange()+ ")不正确，无法继续运行，请先修复该条游戏数据。";
                                return MessageServiceImpl.TaskFinish(
                                        message,
                                        (long) MessageServiceImpl.USERDATA_TASK,
                                        handleState,
                                        returnData
                                );
                            }
                            if(isMul) {      /* 多范围，即 A-B 这样的格式 */
                                logger.debug("--> Mul");
                                try {
                                    /* 先算出范围前后的设备号 */
                                    DevNr p1DevNr = new DevNr();
                                    deviceTask.getDevice(mulOrSingleRange[0], p1DevNr);
                                    DevNr p2DevNr = new DevNr();
                                    deviceTask.getDevice(mulOrSingleRange[1], p2DevNr);
                                    /* 要么像 P1-P2 这样都分组，要么像 1-2 这样都不分组，不允许这样 P1-2 一边分组另一边不分组 */
                                    if(!p1DevNr.getGroupNr().equals(p2DevNr.getGroupNr())){
                                        handleState = Message.HANDLER_FAILURE;
                                        returnData = "游戏数据任务：游戏数据中存在游戏数据范围[" + gd.getDevRange() + "]不正确，无法添加游戏数据，" +
                                                "范围要么都分组，要么都不分组，不允许出现一边分组另一边不分组的范围！";
                                        return MessageServiceImpl.TaskFinish(
                                                message,
                                                (long) MessageServiceImpl.GAMEDATA_TASK,
                                                handleState,
                                                returnData
                                        );
                                    }
                                    Long devNrStart = p1DevNr.getDevNr();
                                    Long devNrEnd = p2DevNr.getDevNr();
                                    logger.debug(" --> " + devNrStart + " | " + devNrEnd + "<-- ");
                                    for (long i = devNrStart; i <= devNrEnd; i++){
                                        logger.debug(" --> " + i);
                                        /* 好的，找到这台游戏数据应该使用的用户数据了！ */
                                        if(queryDevNr.equals(i) && queryGroupNr.equals(p1DevNr.getGroupNr())){
                                            fond = true;
                                            gameData = gd;
                                            break;                                              /* 找到了，没必要停止再往下查找了 */
                                        }
                                    }
                                } catch (Exception e){
                                    handleState = Message.HANDLER_FAILURE;
                                    returnData = "游戏数据任务：游戏数据中有游戏数据范围(" + gd.getDevRange()+ ")不正确，无法继续运行，请先修复该条游戏数据。";
                                    return MessageServiceImpl.TaskFinish(
                                            message,
                                            (long) MessageServiceImpl.USERDATA_TASK,
                                            handleState,
                                            returnData
                                    );
                                }
                            } else {            /* 单范围，即只指出单个游戏数据 */
                                DevNr pDevNr = new DevNr();
                                deviceTask.getDevice(mulOrSingleRange[0], pDevNr);
                                if(queryDevNr.equals(pDevNr.getDevNr()) && queryGroupNr.equals(pDevNr.getGroupNr())){
                                    fond = true;
                                    gameData = gd;
                                    break;                                              /* 找到了，没必要停止再往下查找了 */
                                }
                            }
                        }
                    }
                }
                if(fond){       /* 找到 */
                    gameData.setGameName(gameDao.selectOne(new QueryWrapper<Game>()
                            .select("name")
                            .eq("id", gameData.getGameFK()))
                            .getName());
                    handleState = Message.HANDLER_SUCCESS;
                    returnData = gameDataGson.toJson(gameData);
                } else {        /* 没找到 */
                    handleState = Message.HANDLER_FAILURE;
                    returnData = "游戏数据任务：没有找到该游戏数据的游戏数据" +
                            "[游戏数据号:" + queryBean.getDevNr() + "]" +
                            "的信息，请先通过管理后台中的<游戏数据管理>添加这个游戏数据的调度方案。";
                }
            }

        } catch (Exception e){
            handleState = Message.HANDLER_FAILURE;
            returnData = "游戏数据任务：未给出正常的游戏数据号，无法获取游戏数据";
        }

        /* 返回处理结果 */
        return MessageServiceImpl.TaskFinish(
                message,
                (long) MessageServiceImpl.GAMEDATA_TASK,
                handleState,
                returnData
        );
    }

    private Message do_no_call(Message message) {
        return MessageServiceImpl.TaskFinish(
                message,
                (long) MessageServiceImpl.GAMEDATA_TASK,
                Message.HANDLER_FAILURE,
                "游戏数据任务：你请求了一个不存在的功能服务......"
        );
    }

    @Override
    public PageBean<GameData> findAll(PageBean<GameData> pageBean, GameData queryBean) {
        DbIsUpdate = false;
        /* 分组查询从游戏任务中拿出该游戏的所有游戏数据 */
        if(pageBean == null){
            pageBean = new PageBean<>();
        }
        pageBean.setRecords(gameDataDao.selectAllByGameName(pageBean, queryBean.getGameName()));

        /* 返回结果 */
        return pageBean;
    }

    @Override
    @CacheEvict(key = "#id")
    public int remove(Long id) {
        DbIsUpdate = true;
        return gameDataDao.deleteById(id);
    }

    @Override
    public int save(GameData record) {
        DbIsUpdate = true;
        return gameDataDao.insert(record);
    }

    @Override
    @Cacheable(
            key = "#id",                               /* 缓存key使用用户的id */
            cacheManager = "jsonRedisCacheManager",     /* 使用json缓存管理器 */
            unless = "#result == null"                  /* 当没查到时，不缓存 */
    )
    public GameData findById(Long id) {
        return gameDataDao.selectById(id);
    }

    @Override
    @CachePut(key = "#record.id")
    public int update(GameData record) {
        DbIsUpdate = true;
        return gameDataDao.updateById(record);
    }

    @Override
    public int saveOrUpdate(GameData record) {
        DbIsUpdate = true;
        int rows = 0;
        // 根据是否有id决定是添加还是更新
        if(record.getId() == null){
            rows = save(record);
        } else {
            rows = update(record);
        }
        return rows;
    }
}
